#include <windows.h>
#include "CUserInterface.h"
#include "CLevelEditor.h"
#include "CinematicAdjust.h"
#include "CCinematicThread.h"
#include "CCinematicCommand.h"
#include "IGUIEnvironment.h"
#include "IGUIWindow.h"
#include "IGUIEditBox.h"
#include "IrrlichtDevice.h"
#include "ISceneNode.h"
#include "IFileSystem.h"
#include "IReadFile.h"
#include "IWriteFile.h"
#include "CSceneObject.h"
#include "IXMLWriter.h"

extern CUserInterface* ui;
extern CLevelEditor* editor;

CCinematicAdjust::CCinematicAdjust() 
{
	m_allThreadInfo.clear();
}

CCinematicAdjust::~CCinematicAdjust()
{
	m_allThreadInfo.clear();
}

void CCinematicAdjust::startAdjust( CSceneObject *pObject )
{
	m_Target = pObject;

	createWindow();
}

bool CCinematicAdjust::receiveEvent(const SEvent& event)
{
	switch (event.GUIEvent.EventType)
	{		
	case EGET_ELEMENT_CLOSED:
		if (event.GUIEvent.Caller == m_AdjustWindow)
		{			
			closeDialog();
			return true;
		}
		break;
	case EGET_BUTTON_CLICKED:
		{
			switch (event.GUIEvent.Caller->getID())
			{
			case CINEMATIC_ADJUST_ID_CANCEL_BTN:
				closeDialog();
				return true;
			case CINEMATIC_ADJUST_ID_OK_BTN:
				if( adjustPosition() )
				{
					closeDialog();
				}
				else
				{
					g->guienv->addMessageBox(L"Error!", L"Please specify value!");
				}
				return true;
			}
		}
		break;
	}
	return false;
}

void CCinematicAdjust::closeDialog()
{
	if (m_AdjustWindow)
	{
		m_AdjustWindow->remove();
		m_AdjustWindow = 0;
	}
}

void CCinematicAdjust::createWindow()
{
	const int CINEMATIC_ADJUST_WINDOW_WIDTH	= 300;

	const dimension2d<u32> screen = g->driver->getScreenSize();

	stringw caption = stringw(L"Adjust Cinematic ID: ") + stringw(m_Target->m_SceneNode->getID());
	rect<s32> r ( 400, 200,	660, 360);		
	m_AdjustWindow = g->guienv->addWindow( r , false, caption.c_str(), 0, WND_ID_CINEMATIC_ADJUST);

	int posX = 50;
	int posY = 40;
	int widthX = 20;
	int heightY = 15;
	int spaceY = 25;
	int textWidth = 160;
	int textHeight = 20;
	g->guienv->addStaticText(L"X:" ,rect<s32>( posX, posY, posX+widthX, posY+heightY), false , false , m_AdjustWindow);
	m_xEdit = g->guienv->addEditBox(L"0.0",rect<s32>( posX+widthX, posY, posX+widthX+textWidth, posY+textHeight),true,m_AdjustWindow );
	posY+=spaceY;

	g->guienv->addStaticText(L"Y:" ,rect<s32>( posX, posY, posX+widthX, posY+heightY), false , false , m_AdjustWindow);
	m_yEdit = g->guienv->addEditBox(L"0.0",rect<s32>( posX+widthX, posY, posX+widthX+textWidth, posY+textHeight),true,m_AdjustWindow );
	posY+=spaceY;

	g->guienv->addStaticText(L"Z:" ,rect<s32>( posX, posY, posX+widthX, posY+heightY), false , false , m_AdjustWindow);
	m_zEdit = g->guienv->addEditBox(L"0.0",rect<s32>( posX+widthX, posY, posX+widthX+textWidth, posY+textHeight),true,m_AdjustWindow );
	
	int xstart = 80;
	int ybottom = 120;
	int xwidth = 60;
	int yheight = 20;
	int spacing = 20;
	g->guienv->addButton(rect<s32>(xstart, ybottom ,xstart+xwidth, ybottom+ yheight), m_AdjustWindow,
		CINEMATIC_ADJUST_ID_OK_BTN, L"OK");
	xstart+=xwidth + spacing;
	g->guienv->addButton(rect<s32>(xstart, ybottom ,xstart+xwidth, ybottom+ yheight), m_AdjustWindow,
		CINEMATIC_ADJUST_ID_CANCEL_BTN, L"Cancel");
}


bool CCinematicAdjust::adjustPosition()
{
	irr::core::stringw xText =  m_xEdit->getText();
	irr::core::stringw yText =  m_yEdit->getText();
	irr::core::stringw zText =  m_zEdit->getText();

	float x = _wtof(xText.c_str() );
	float y = _wtof(yText.c_str() );
	float z = _wtof(zText.c_str() );

	if( (x == 0.0f) &&
		(y == 0.0f) &&
		(z == 0.0f) )
	{
		m_xEdit->setTabStop(true);		
		return false;
	}

	irr::core::stringc tmp;
	tmp = "x = ";
	tmp +=x;
	tmp += ";y = ";
	tmp += y;
	tmp += ";z = ";
	tmp += z;
	tmp +="\n";
	OutputDebugStringA( tmp.c_str() );

	irr::core::stringc fileName = m_Target->m_UserData->getAttributeAsString("!ScriptFile");
	OutputDebugStringA( fileName.c_str() );


	irr::io::IFileSystem* pFs = g->device->getFileSystem();
	if( !pFs->existFile(fileName ) )
	{
		return false;
	}

	IReadFile* pFile = pFs->createAndOpenFile( fileName.c_str() );
	long fileSize =  pFile->getSize();
	irr::c8 *pBuffer = new irr::c8[fileSize];
	pFile->read(pBuffer,fileSize);

	irr::core::stringc fileNameBack = fileName;
	fileNameBack+=".bak";

	IWriteFile* pFileBack = pFs->createAndWriteFile( fileNameBack.c_str() );
	pFileBack->write(pBuffer,fileSize);
	delete[] pBuffer;

	pFile->drop();
	pFileBack->drop();

	loadAndAdjustCinematic(fileName.c_str(),x,y,z);

	SaveAgainCinematic(fileName.c_str());
	return true;
}

void CCinematicAdjust::loadAndAdjustCinematic(const irr::c8 *name, float x, float y, float z)
{
	CCinematicThread* thread = 0;
	CCinematicCommand* cinematicCmd = 0;
	array<CCinematicCommand*>* list = 0;

	m_allThreadInfo.clear();

	IXMLReader* xml = g->device->getFileSystem()->createXMLReader(name);
	
	int currentColumn = -1;

	if (!xml)
	{
		return;
	}
	
	int bModify = 0;
	while (xml->read())
	{
		switch(xml->getNodeType())
		{
			case EXN_ELEMENT:
			{
				if (!wcscmp(L"cinematicThread", xml->getNodeName()))
				{
					thread = new CCinematicThread(xml->getAttributeValueAsInt(L"type")); 
					thread->m_Name = xml->getAttributeValueSafe(L"name");
					s32 objID = xml->getAttributeValueAsInt(L"object");
					CSceneObject* obj = editor->getSceneObjectFromID(objID);
					
					if (obj)
					{
						thread->m_Obj = obj;
					}

					THREAD_INFO info;
					info.m_thread = thread;
					m_allThreadInfo.push_back( info );
				}
				else if (!wcscmp(L"time", xml->getNodeName()))
				{
					list = new array<CCinematicCommand*>;
					currentColumn = xml->getAttributeValueAsInt(L"stamp");
				} 
				else if (!wcscmp(L"command", xml->getNodeName()))
				{
					cinematicCmd = new CCinematicCommand();
					cinematicCmd->m_id = xml->getAttributeValueAsInt(L"id");
					cinematicCmd->m_Name = xml->getAttributeValue(L"name");
					if( cinematicCmd->m_Name == "MoveObject" )
					{
						bModify = 1;
					}
					else if( cinematicCmd->m_Name =="ChangeCamera" )
					{
						bModify = 2;
					}
				}								

				if (!wcscmp(L"attributes", xml->getNodeName()))
				{
					if (cinematicCmd)
					{
						cinematicCmd->m_Attributes->read(xml, false);				

						//discard atributes that no longer match current command
						if( bModify>0 )
						{
							switch( bModify )
							{
							case 1:
								{
									irr::core::vector3df pos = cinematicCmd->m_Attributes->getAttributeAsVector3d("pos");
									pos.X += x;
									pos.Y += y;
									pos.Z += z;
									cinematicCmd->m_Attributes->setAttribute("pos",pos);

									irr::core::vector3df abspos = cinematicCmd->m_Attributes->getAttributeAsVector3d("abspos");
									abspos.X += x;
									abspos.Y += y;
									abspos.Z += z;
									cinematicCmd->m_Attributes->setAttribute("abspos",abspos);
								}
								break;
							case 2:
								{
									irr::core::vector3df pos = cinematicCmd->m_Attributes->getAttributeAsVector3d("target");
									pos.X += x;
									pos.Y += y;
									pos.Z += z;
									cinematicCmd->m_Attributes->setAttribute("target",pos);
								}
								break;
							}
							bModify = 0;
						}
					}
				}								
			}
			break;

			case EXN_ELEMENT_END:
			{				
				if (!wcscmp(L"time", xml->getNodeName()))
				{
					//push the list in the table 
					int row = m_allThreadInfo.size() - 1;
					
					if (row >= 0 && currentColumn >= 0)
					{
						m_allThreadInfo[m_allThreadInfo.size() -1].m_allCommandList.push_back(list);
						m_allThreadInfo[m_allThreadInfo.size() -1].m_stampList.push_back(currentColumn);
						list = 0;
						currentColumn = -1;
					}
				}

				if (!wcscmp(L"command", xml->getNodeName()))
				{
					//push this command in the list 
					(*list).push_back(cinematicCmd);
					cinematicCmd = 0;
				}
				
				if (!wcscmp(L"cinematicThread", xml->getNodeName()))
				{
					thread = 0;
				}
				break;
			}
			
		}
	}

	xml->drop();
}

void CCinematicAdjust::SaveAgainCinematic(const irr::c8 *name)
{
	IXMLWriter* xml = g->device->getFileSystem()->createXMLWriter(name);

	if (!xml)
		return;
	
	xml->writeXMLHeader(); 
	xml->writeLineBreak();
	
	xml->writeComment(L"Cinematic file");	
	xml->writeLineBreak();

	int rows = m_allThreadInfo.size();	

	CCinematicThread* cmd = 0;

	for (u32 i = 0 ; i < rows ; i++ )
	{		
		cmd = static_cast<CCinematicThread*>(m_allThreadInfo[i].m_thread);
		if (cmd)
		{
			s32 objId = -1;
			if (cmd->m_Obj)
			{
				objId = cmd->m_Obj->m_SceneNode->getID();
			}
			xml->writeLineBreak();
			xml->writeElement(L"cinematicThread", false , 
				L"type" , stringw(cmd->m_Type).c_str() , 
				L"name" , cmd->m_Name.c_str() ,
				L"object" , stringw(objId).c_str() );
			xml->writeLineBreak();
			//all commands here 			
		}

		int columns = m_allThreadInfo[i].m_allCommandList.size();
		for (u32 j = 0 ; j < columns ; j++ )
		{
			void * data = m_allThreadInfo[i].m_allCommandList[j];
			array<CCinematicCommand*>* commands = static_cast<array<CCinematicCommand*>*>(data);
			if (commands)
			{				
				if( (*commands).size() > 0 )
				{
					xml->writeElement(L"time", false, L"stamp", stringw( m_allThreadInfo[i].m_stampList[j] ).c_str() );
					xml->writeLineBreak();

					for (u32 k = 0 ; k < (*commands).size(); k++)
					{
						CCinematicCommand* cmd = (*commands)[k];
						xml->writeElement(L"command", false, L"name", cmd->m_Name.c_str() , L"id" , stringw(cmd->m_id).c_str());
						xml->writeLineBreak();
						cmd->m_Attributes->write(xml, false);
						xml->writeClosingTag(L"command");
						xml->writeLineBreak();
					}	

					xml->writeClosingTag(L"time");
					xml->writeLineBreak();
					delete commands;					
				}
				else
				{
					printf("size of command list is 0.\n");
				}
			}
			else
			{
				printf("command list is NULL\n");
			}
		}

		m_allThreadInfo[i].m_allCommandList.clear();
		m_allThreadInfo[i].m_stampList.clear();

		if (cmd)
		{
			xml->writeClosingTag(L"cinematicThread");
			xml->writeLineBreak();
			delete cmd;			
		}
	}
	
	m_allThreadInfo.clear();
	xml->drop();
}